#include "RenderThread.h"
#include <windows.h>
#include "../Utils/LogUtil.h"
#include "../Core/CoreGlobals.h"
#include "../Utils/TimeUtil.h"
#include "../Thread/ThreadManager.h"
#include "../Render/Shader/ShaderMap.h"
#include "../Misc/Crc.h"
#include "RenderState.h"
#include "../Thread/TaskGraph.h"

VulkanRHI* GVulkanRHI;
RenderState* GRenderState;

RenderThread::RenderThread(std::shared_future<void>& startPromise, ThreadManager* threadManager) : TaskThread()
{
	mStartFuture = startPromise;
	mThreadManager = threadManager;
}

bool RenderThread::Init()
{
	assert(TaskThread::Init());

	// Init vulkan
	GVulkanRHI = new VulkanRHI();
	GVulkanRHI->Init();

	// Compile shader must before RenderState init
	GShaderMap->CompileShader();

	// Init State
	GRenderState = new RenderState();
	GRenderState->Init();

	SetEvent(mThreadManager->mWaitRenderInit);

	// Must before gameState init
	LOGI(L"RenderThread Init Pre");

	WaitForSingleObject(mThreadManager->mWaitGameInit, INFINITE);
	CloseHandle(mThreadManager->mWaitGameInit);
	mThreadManager->mWaitGameInit = nullptr;

	// Must before gameThread run
	LOGI(L"Render Init Post");

	return true;
}

uint32 RenderThread::Run()
{
	// lock until main thread run
	mStartFuture.get();

	while (true)
	{	
		GRenderState->WaitForGame();

		if (mThreadManager->IsTimeToExit())
		{
			break;
		}

		GRenderState->MainLoop();

		ProcessTasksUntilReturn();

		GRenderState->SubmitFrame();

		GRenderState->NextFrame();
	}

	GRenderState->Stop();

	LOGI(L"Render Stop");

	// Run this to clean all render command call by game thread
	ProcessTasksForCleanup();

	SetEvent(mThreadManager->mWaitRenderExit);

	WaitForSingleObject(mThreadManager->mWaitGameExit, INFINITE);
	CloseHandle(mThreadManager->mWaitGameExit);
	mThreadManager->mWaitGameExit = nullptr;

	// Run to gc all rhiResource by game thread exit
	ProcessTasksForGC();

	// Must later game exit
	LOGI(L"Render Exit");

	GRenderState->Exit();

	return 0;
}

void RenderThread::Stop()
{
	TaskThread::Stop();

	delete GRenderState;
	GRenderState = nullptr;

	delete GShaderMap;
	GShaderMap = nullptr;

	// destroy vulkan
	GVulkanRHI->Exit();
	delete GVulkanRHI;
	GVulkanRHI = nullptr;
}

void RenderThread::Exit()
{
	TaskThread::Exit();
}

void RenderThread::ProcessTasksUntilReturn()
{
	while (!mQuitForReturn && mQueueTasks.Size() > 0)
	{
		BaseGraphTask* task = nullptr;
		mQueueTasks.Pop(&task);
		if (task != nullptr)
		{
			task->ExecuteTask();
			if (GRenderState->IsSubmitTexture())
			{
				GRenderState->ResetSubmitState();
				return;
			}
		}
	}
}

void RenderThread::ProcessTasksForCleanup()
{
	while (!mQuitForReturn && mQueueTasks.Size() > 0)
	{
		BaseGraphTask* task = nullptr;
		mQueueTasks.Pop(&task);
	}
}

void RenderThread::ProcessTasksForGC()
{
	while (!mQuitForReturn && mQueueTasks.Size() > 0)
	{
		BaseGraphTask* task = nullptr;
		mQueueTasks.Pop(&task);
		if (task != nullptr)
		{
			task->ExecuteTask();
		}
	}
}

